/*
 * shdr.c
 *
 *  Created on: Sep 9, 2012
 *      Author: aortegag
 */

#include <stdlib.h>
#include <string.h>
#include "shdr.h"
#include "common.h"
#include "logging.h"

// \var Holds the array of section header names
static char * sh_names = 0;

Elf64_Shdr *allocate_section_header_table(const Elf64_Ehdr *ehdr)
{
	if(!ehdr)
		return 0;

	if(ehdr->e_shnum == 0)
		return 0;

	return (Elf64_Shdr *)malloc(ehdr->e_shentsize * ehdr->e_shnum);
}



elf_code_t read_section_header_table(FILE *fd, const Elf64_Ehdr *ehdr, Elf64_Shdr *shdr) {

	if (!ehdr)
		return ELF_SHDR_INVALID_ERROR;

	if (ehdr->e_shoff == 0)
		return ELF_SHDR_TABLE_NOT_FOUND;

	if( !shdr )
		return ELF_SHDR_ALLOC_ERROR;

	rewind(fd);
	fseek(fd, ehdr->e_shoff, SEEK_SET);

	size_t read = fread(shdr, ehdr->e_shentsize, ehdr->e_shnum, fd);

	if( ferror( fd ))
		return ELF_ERROR;

	if (read != ehdr->e_shnum)
		return ELF_SHDR_READ_ERROR;

	return ELF_SUCCESS;
}



/**
 * \brief Pretty prints the Section Header table
 *
 * \note This function assumes a section header has been stored
 *  in the Elf structure.
 */
void	pretty_print_shdr(FILE *fd, const Elf64_Ehdr *ehdr,  const Elf64_Shdr *shdr)
{
	if(!shdr){
		LOG_WARN("Invalid section header table.\n");
		return;
	}
	if(ehdr->e_shnum == 0){
		LOG_INFO("Section Header table not available.\n");
		return;
	}
	LOG_INFO("There are %d section headers, starting at offset %d (%#08x)\n",\
			ehdr->e_shnum, (unsigned int)ehdr->e_shoff,\
			(unsigned int)ehdr->e_shoff);

	int i;
	const Elf64_Shdr *s = shdr;
	const char *names = get_section_header_names(fd, shdr, ehdr->e_shnum);

	if(!names){
		LOG_ERR("Invalid list of names.\n");
		return;
	}


	LOG_INFO("Section headers table.\n");
	LOG_INFO("===================================="\
			"=========================================\n");
	LOG_INFO(\
			"Indx\tName\t\t   Type\t     Address\t\tOffset\t   Alignment\n"
			"\tSize\t\t   EntSize   Flags\t\tLink\t   Info\t\n");
	LOG_INFO("===================================="\
			"=========================================\n");
	for(i = 0; i < ehdr->e_shnum; i++){
		char *type_n = section_type(s[i].sh_type);
		/* Things to print:
		 * Index	Name			Type	Address		Offset		Size
		 * 			EntrySize		Flags	Link		Info		Alignment
		 */
		LOG_INFO("[%d]\t"\
				"%-18s "\
				"%-8s  "\
				"%#016x\t"\
				"%#08x   "\
				"%ld\n",\
				i, &names[s[i].sh_name],\
				type_n,\
				(unsigned int)s[i].sh_addr,\
				(unsigned int)s[i].sh_offset,\
				(long int)s[i].sh_addralign);
		LOG_INFO("                                    "\
					"                                         \n");
		LOG_INFO("\t%#016x   "\
				"%#08x  "\
				"%#016x\t"\
				"%ld\t   "\
				"%ld\t"\
				"\n",\
				(unsigned int)s[i].sh_size,\
				(unsigned int)s[i].sh_entsize,\
				(unsigned int)s[i].sh_flags,\
				(long int)s[i].sh_link,\
				(long int)s[i].sh_info\
				);
			LOG_INFO("===================================="\
					"=========================================\n");
	}
}


const Elf64_Shdr *get_section_header_by_name(FILE *fd, const Elf64_Shdr *table,\
							const Elf64_Half sh_num, const char* section_name)
{
	Elf64_Half i = 0;

	const char * names = get_section_header_names(fd, table, sh_num);

	// We have the section header string table, let's find what we are looking for.
	for(i = 0; i < sh_num; i++){
		if(strcmp(&names[table[i].sh_name], section_name) == 0){
			return &table[i];
		}
	}

	return NULL;
}

const Elf64_Shdr *get_section_header_string_table(FILE *fd, const Elf64_Shdr *table,\
								const Elf64_Half sh_num)
{
	Elf64_Half i = 0;
	char * names = 0;

	for(i = 0; i < sh_num; i++) {
		if(table[i].sh_type == SHT_STRTAB){
			// There could be several string table sections, we need to verify
			// we are in the section header string table.
			Elf64_Word ndx = table[i].sh_name;
			names = (char*) malloc(table[i].sh_size);
			memset(names, 0, table[i].sh_size);
			fseek(fd, table[i].sh_offset, SEEK_SET);
			size_t read = fread( names, table[i].sh_size, 1, fd);

			if(read != 1)
				return NULL;
			if(strcmp(&names[ndx],".shstrtab") == 0){
				free(names);
				return &table[i];
			}
			free(names);
			names = 0;
		}
	}
	return NULL;
}

const char * get_section_header_names(FILE *fd, const Elf64_Shdr *table,\
					const Elf64_Half sh_num)
{
	if( sh_names ){
		return sh_names;
	}

	const Elf64_Shdr * sh_strtab = get_section_header_string_table(fd, table, sh_num);

	if(!sh_strtab ){
		LOG_ERR("Invalid section header string table.\n");
		return NULL;
	}

	char * names = (char*) malloc(sh_strtab->sh_size);
	memset(names, 0,sh_strtab->sh_size );
	fseek(fd, sh_strtab->sh_offset, SEEK_SET);
	size_t read = fread( names, sh_strtab->sh_size, 1, fd);

	if(read != 1){
		return NULL;
	}

	sh_names = names;
	return sh_names;
}

void	cleanup_section_names()
{
	if(sh_names){
		free((void*)sh_names);
		sh_names = NULL;
	}
	return;
}

void	print_section_contents(FILE *fd, const Elf64_Shdr *shdr)
{
	if(shdr->sh_size == 0){
		LOG_INFO("Skipping 0 bytes section.\n");
		return;
	}

	unsigned char * buf = NULL;
	buf = (unsigned char*)malloc(shdr->sh_size);
	if(!buf){
		LOG_INFO("Error allocating memory for section.\n");
	}
	buf = memset(buf, 0, shdr->sh_size);

	fseek(fd, shdr->sh_offset, SEEK_SET);
	size_t read = fread(buf, shdr->sh_size, 1, fd);

	if(read != 1){
		LOG_ERR("Could not read section contents.\n");
		return;
	}

	union dword_t{
		unsigned int dword;
		unsigned char byte[4];
	}*b = NULL;
	b = (union dword_t*) buf;
	unsigned int i = 0;
	Elf64_Addr offset = shdr->sh_addr;
	while(i < shdr->sh_size){
		// First dword
		LOG_INFO("%08x: ", (unsigned int)offset);
		LOG_INFO("%02x",b->byte[0]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[1]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[2]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x ",b->byte[3]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		b++;
		// Second dword
		LOG_INFO("%02x",b->byte[0]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[1]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[2]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x ",b->byte[3]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		b++;
		// Third dword
		LOG_INFO("%02x",b->byte[0]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[1]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[2]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x ",b->byte[3]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		b++;
		// Fourth dword
		LOG_INFO("%02x",b->byte[0]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[1]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x",b->byte[2]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		LOG_INFO("%02x\n",b->byte[3]);i++;if( i >= shdr->sh_size){LOG_INFO("\n");break;}
		b++;
		// Printing 4 dwords per row
		offset += 16;
	}

	if(buf) free(buf);
}

void	print_all_section_contents(FILE *fd,\
		const Elf64_Half sh_num, const Elf64_Shdr *table)
{
	Elf64_Half i;
	const char * names = get_section_header_names(fd, table, sh_num);
	for(i = 0; i < sh_num; i++){
		LOG_INFO("Contents of section %s: %d bytes\n",\
				&names[table[i].sh_name], (int)table[i].sh_size);
		print_section_contents(fd, &table[i]);
	}
}

